﻿Public Class CDiffLogic

#Region "Generic"


	Public Shared Function Diff(sourceIds As List(Of Integer), targetIds As List(Of Integer)) As CDiff_Int
		Dim d As New CDiff_Int
		For Each i As Integer In sourceIds
			If Not targetIds.Contains(i) Then
				d.SourceOnly.Add(i)
			End If
		Next
		For Each i As Integer In targetIds
			If Not sourceIds.Contains(i) Then
				d.TargetOnly.Add(i)
			Else
				d.Matching.Add(i)
			End If
		Next
		Return d
	End Function
	Public Shared Function Diff(sourceIds As List(Of Long), targetIds As List(Of Long)) As CDiff_Long
		Dim d As New CDiff_Long
		For Each i As Long In sourceIds
			If Not targetIds.Contains(i) Then
				d.SourceOnly.Add(i)
			End If
		Next
		For Each i As Long In targetIds
			If Not sourceIds.Contains(i) Then
				d.TargetOnly.Add(i)
			Else
				d.Matching.Add(i)
			End If
		Next
		Return d
	End Function

	Public Shared Function Diff_ByHash(source As CFolderHash, target As CFolderHash) As CDiff_FileHash_ByHash
		Return New CDiff_FileHash_ByHash(source, target)
	End Function
	Public Shared Function Diff_ByNames(source As CFolderHash, target As CFolderHash) As CDiff_FileHash_ByName
		Return New CDiff_FileHash_ByName(source, target)
	End Function

	Public Shared Function Diff(source As List(Of Guid), target As List(Of Guid)) As CDiff_Guid
		Dim d As New CDiff_Guid
		For Each i As Guid In source
			If Not target.Contains(i) Then
				d.SourceOnly.Add(i)
			End If
		Next
		For Each i As Guid In target
			If Not source.Contains(i) Then
				d.TargetOnly.Add(i)
			Else
				d.Matching.Add(i)
			End If
		Next
		Return d
	End Function
	Public Shared Function Diff(source As List(Of String), target As List(Of String)) As CDiff_String
		Dim d As New CDiff_String
		For Each i As String In source
			If Not target.Contains(i) Then
				d.SourceOnly.Add(i)
			End If
		Next
		For Each i As String In target
			If Not source.Contains(i) Then
				d.TargetOnly.Add(i)
			Else
				d.Matching.Add(i)
			End If
		Next
		Return d
	End Function



	'Old-style (schema-info)
	Public Shared Sub Diff(this As List(Of String), ref As List(Of String), ByRef missing As List(Of String), ByRef added As List(Of String), ByRef same As List(Of String))
        For Each i As String In this
            If Not ref.Contains(i) Then
                added.Add(i)
            Else
                same.Add(i)
            End If
        Next
        For Each i As String In ref
            If Not this.Contains(i) Then missing.Add(i)
        Next
    End Sub

    Public Shared Sub Diff(this As Dictionary(Of String, List(Of String)), ref As Dictionary(Of String, List(Of String)), ByRef missing As Dictionary(Of String, List(Of String)), ByRef added As Dictionary(Of String, List(Of String)), ByRef same As Dictionary(Of String, List(Of String)), ByRef diff As Dictionary(Of String, List(Of String)))
        For Each i As String In this.Keys
            If Not ref.ContainsKey(i) Then
                added.Add(i, this(i))
            ElseIf CUtilities.ListToString(this(i)) = CUtilities.ListToString(ref(i)) Then
                same.Add(i, this(i))
            Else
                Dim m As New List(Of String)
                Dim a As New List(Of String)
                Dim s As New List(Of String)
                CDiffLogic.Diff(this(i), ref(i), m, a, s)
                diff.Add(i, Summarise(m, a, s))
            End If
        Next
        For Each i As String In ref.Keys
            If Not this.ContainsKey(i) Then missing.Add(i, ref(i))
        Next
    End Sub
    Public Shared Function Summarise(missing As List(Of String), added As List(Of String), same As List(Of String)) As List(Of String)
        Dim temp As New List(Of String)
        For Each i As String In missing
            temp.Add("MISSING: " & i)
        Next
        For Each i As String In added
            temp.Add("EXTRA: " & i)
        Next
        temp.AddRange(same)
        Return temp
    End Function
#End Region


#Region "DataTable"
	Public Shared Function Diff_MD5(sourceIndex As Dictionary(Of Guid, List(Of Integer)), targetIndex As Dictionary(Of Guid, List(Of Integer))) As CDiff_Guid
		Return Diff(New List(Of Guid)(sourceIndex.Keys), New List(Of Guid)(targetIndex.Keys))
	End Function
	Public Shared Function Diff_MD5(sourceIndex As Dictionary(Of Guid, List(Of Long)), targetIndex As Dictionary(Of Guid, List(Of Long))) As CDiff_Guid
		Return Diff(New List(Of Guid)(sourceIndex.Keys), New List(Of Guid)(targetIndex.Keys))
	End Function
	Public Shared Function Diff_MD5(sourceIndex As Dictionary(Of Guid, List(Of String)), targetIndex As Dictionary(Of Guid, List(Of String))) As CDiff_Guid
		Return Diff(New List(Of Guid)(sourceIndex.Keys), New List(Of Guid)(targetIndex.Keys))
	End Function
	Public Shared Function Diff_MD5(sourceIndex As Dictionary(Of Guid, List(Of Guid)), targetIndex As Dictionary(Of Guid, List(Of Guid))) As CDiff_Guid
		Return Diff(New List(Of Guid)(sourceIndex.Keys), New List(Of Guid)(targetIndex.Keys))
	End Function
	Public Shared Function Diff_PKInt(sourceIndex As Dictionary(Of Integer, DataRow), targetIndex As Dictionary(Of Integer, DataRow)) As CDiff_Int
		Return Diff(New List(Of Integer)(sourceIndex.Keys), New List(Of Integer)(targetIndex.Keys))
	End Function
	Public Shared Function Diff_PKLong(sourceIndex As Dictionary(Of Long, DataRow), targetIndex As Dictionary(Of Long, DataRow)) As CDiff_Long
		Return Diff(New List(Of Long)(sourceIndex.Keys), New List(Of Long)(targetIndex.Keys))
	End Function
	Public Shared Function Diff_PKGuid(sourceIndex As Dictionary(Of Guid, DataRow), targetIndex As Dictionary(Of Guid, DataRow)) As CDiff_Guid
		Return Diff(New List(Of Guid)(sourceIndex.Keys), New List(Of Guid)(targetIndex.Keys))
	End Function
	Public Shared Function Diff_PKStr(sourceIndex As Dictionary(Of String, DataRow), targetIndex As Dictionary(Of String, DataRow)) As CDiff_String
		Return Diff(New List(Of String)(sourceIndex.Keys), New List(Of String)(targetIndex.Keys))
	End Function
	Public Shared Function Diff_Exact(d As CDataDiff_PkInt) As List(Of Integer)
		Dim exact As New List(Of Integer)
		For Each i As Guid In d.DiffOnRowHash.Matching
			Dim ids As List(Of Integer) = d.SourceRowHashIndex(i)
			Dim idt As List(Of Integer) = d.TargetRowHashIndex(i)
			For Each j As Integer In ids
				If idt.Contains(j) Then exact.Add(j)
			Next
		Next
		Return exact
	End Function
	Public Shared Function Diff_Exact(d As CDataDiff_PkLong) As List(Of Long)
		Dim exact As New List(Of Long)
		For Each i As Guid In d.DiffOnRowHash.Matching
			Dim ids As List(Of Long) = d.SourceRowHashIndex(i)
			Dim idt As List(Of Long) = d.TargetRowHashIndex(i)
			For Each j As Long In ids
				If idt.Contains(j) Then exact.Add(j)
			Next
		Next
		Return exact
	End Function
	Public Shared Function Diff_Exact(d As CDataDiff_PkGuid) As List(Of Guid)
		Dim exact As New List(Of Guid)
		For Each i As Guid In d.DiffOnRowHash.Matching
			Dim ids As List(Of Guid) = d.SourceRowHashIndex(i)
			Dim idt As List(Of Guid) = d.TargetRowHashIndex(i)
			For Each j As Guid In ids
				If idt.Contains(j) Then exact.Add(j)
			Next
		Next
		Return exact
	End Function
	Public Shared Function Diff_Exact(d As CDataDiff_PkString) As List(Of String)
		Dim exact As New List(Of String)
		For Each i As Guid In d.DiffOnRowHash.Matching
			Dim ids As List(Of String) = d.SourceRowHashIndex(i)
			Dim idt As List(Of String) = d.TargetRowHashIndex(i)
			For Each j As String In ids
				If idt.Contains(j) Then exact.Add(j)
			Next
		Next
		Return exact
	End Function
	Public Shared Function Diff_Exact(d As CDataDiff_PkIntInt) As List(Of String)
		Dim exact As New List(Of String)
		For Each i As Guid In d.DiffOnRowHash.Matching
			Dim ids As List(Of String) = d.SourceRowHashIndex(i)
			Dim idt As List(Of String) = d.TargetRowHashIndex(i)
			For Each j As String In ids
				If idt.Contains(j) Then exact.Add(j)
			Next
		Next
		Return exact
	End Function


	Public Shared Function TableIndex_PkInt(dt As DataTable) As Dictionary(Of Integer, DataRow)
		Dim dict As New Dictionary(Of Integer, DataRow)(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk As Integer = CAdoData.GetInt(i, 0)
			dict.Add(pk, i)
		Next
		Return dict
	End Function
	Public Shared Function TableIndex_PkGuid(dt As DataTable) As Dictionary(Of Guid, DataRow)
		Dim dict As New Dictionary(Of Guid, DataRow)(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			dict.Add(CAdoData.GetGuid(i, 0), i)
		Next
		Return dict
	End Function
	Public Shared Function TableIndex_PkStr(dt As DataTable) As Dictionary(Of String, DataRow)
		Dim dict As New Dictionary(Of String, DataRow)(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk As String = CAdoData.GetStr(i, 0)
			dict.Add(pk, i)
		Next
		Return dict
	End Function
	Public Shared Function TableIndex_PkLong(dt As DataTable) As Dictionary(Of Long, DataRow)
		Dim dict As New Dictionary(Of Long, DataRow)(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk As Long = CAdoData.GetLong(i, 0)
			dict.Add(pk, i)
		Next
		Return dict
	End Function
	Public Shared Function TableIndex_PkIntInt(dt As DataTable, pkName1 As String, pkName2 As String) As Dictionary(Of String, DataRow)
		Dim dict As New Dictionary(Of String, DataRow)(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk1 As Integer = CAdoData.GetInt(i, pkName1)
			Dim pk2 As Integer = CAdoData.GetInt(i, pkName2)
			Dim pk As String = String.Concat(pk1, "_", pk2)
			dict.Add(pk, i)
		Next
		Return dict
	End Function


	Public Shared Function RowHashing_PkInt(dt As DataTable, Optional original As DataTable = Nothing) As Dictionary(Of Guid, List(Of Integer))
		'Account for different order of columns
		Dim mapping As Dictionary(Of Integer, Integer) = Nothing
		If Not IsNothing(original) Then
			Dim sameOrder As Boolean = True
			mapping = New Dictionary(Of Integer, Integer)(dt.Columns.Count)
			For i As Integer = 0 To dt.Columns.Count - 1
				Dim col As DataColumn = dt.Columns(i)
				If col.ColumnName = "__hash__" Then Continue For
				Dim orig As Integer = original.Columns.IndexOf(col.ColumnName)
				mapping.Add(orig, i)
				If orig <> i Then sameOrder = False
			Next
			If sameOrder Then mapping = Nothing
		End If

		If Not dt.Columns.Contains("__hash__") Then dt.Columns.Add("__hash__", GetType(String))

		'Hash the rows (Except for pk)
		Dim dict As New Dictionary(Of Guid, List(Of Integer))(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk As Integer = CAdoData.GetInt(i, 0)

			'Remove pk, fix null
			Dim data As New List(Of Object)(i.ItemArray)
			If Not IsNothing(mapping) Then
				Dim temp As New List(Of Object)(data)
				For j As Integer = 0 To data.Count - 1
					If j >= mapping.Count Then Exit For
					temp(j) = data(mapping(j))
				Next
				data = temp
			End If


			'Fix nulls
			For j As Integer = 0 To data.Count - 1
				If TypeOf (data(j)) Is DBNull Then data(j) = Nothing
				If dt.Columns(j).ColumnName = "TimeStamp" Then data(j) = String.Empty
			Next
			For j As Integer = 0 To data.Count - 1
				If IsNothing(data(j)) Then data(j) = String.Empty
				If TypeOf data(j) Is DateTimeOffset Then
					Dim dto As DateTimeOffset = CType(data(j), DateTimeOffset)
					data(j) = dto.ToString()
				End If
			Next

			'Remove pk
			data.RemoveAt(0)

			'Compute data-only hash
			Dim blob As Byte() = CProto.Serialise(data) 'CSerialise.Pack(data.ToArray, ESerialisation.Protobuf)
			Dim hash As Guid = CBinary.MD5_(blob)
			i("__hash__") = CBinary.ToBase64(hash)

			'Store
			Dim list As List(Of Integer) = Nothing
			If Not dict.TryGetValue(hash, list) Then
				list = New List(Of Integer)(1)
				dict.Add(hash, list)
			End If
			list.Add(pk)
		Next
		Return dict
	End Function

	Public Shared Function RowHashing_PkLong(dt As DataTable, Optional original As DataTable = Nothing) As Dictionary(Of Guid, List(Of Long))
		'Account for different order of columns
		Dim mapping As Dictionary(Of Integer, Integer) = Nothing
		If Not IsNothing(original) Then
			Dim sameOrder As Boolean = True
			mapping = New Dictionary(Of Integer, Integer)(dt.Columns.Count)
			For i As Integer = 0 To dt.Columns.Count - 1
				Dim col As DataColumn = dt.Columns(i)
				If col.ColumnName = "__hash__" Then Continue For
				Dim orig As Integer = original.Columns.IndexOf(col.ColumnName)
				mapping.Add(orig, i)
				If orig <> i Then sameOrder = False
			Next
			If sameOrder Then mapping = Nothing
		End If

		If Not dt.Columns.Contains("__hash__") Then dt.Columns.Add("__hash__", GetType(String))

		'Hash the rows (Except for pk)
		Dim dict As New Dictionary(Of Guid, List(Of Long))(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk As Long = CAdoData.GetLong(i, 0)

			'Remove pk, fix null
			Dim data As New List(Of Object)(i.ItemArray)
			If Not IsNothing(mapping) Then
				Dim temp As New List(Of Object)(data)
				For j As Integer = 0 To data.Count - 1
					If j >= mapping.Count Then Exit For
					temp(j) = data(mapping(j))
				Next
				data = temp
			End If

			'Remove pk
			data.RemoveAt(0)

			'Fix nulls
			For j As Integer = 0 To data.Count - 1
				If TypeOf (data(j)) Is DBNull Then data(j) = Nothing
				If dt.Columns(j).ColumnName = "TimeStamp" Then data(j) = Nothing
			Next
			For j As Integer = 0 To data.Count - 1
				If IsNothing(data(j)) Then data(j) = String.Empty
				If TypeOf data(j) Is DateTimeOffset Then
					Dim dto As DateTimeOffset = CType(data(j), DateTimeOffset)
					data(j) = dto.ToString()
				End If
			Next

			'Compute data-only hash
			Dim blob As Byte() = CProto.Serialise(data) 'CSerialise.Pack(data.ToArray, ESerialisation.Protobuf)
			Dim hash As Guid = CBinary.MD5_(blob)
			i("__hash__") = CBinary.ToBase64(hash)

			'Store
			Dim list As List(Of Long) = Nothing
			If Not dict.TryGetValue(hash, list) Then
				list = New List(Of Long)(1)
				dict.Add(hash, list)
			End If
			list.Add(pk)
		Next
		Return dict
	End Function
	Public Shared Function RowHashing_PkStr(dt As DataTable, Optional original As DataTable = Nothing) As Dictionary(Of Guid, List(Of String))
		'Account for different order of columns
		Dim mapping As Dictionary(Of Integer, Integer) = Nothing
		If Not IsNothing(original) Then
			Dim sameOrder As Boolean = True
			mapping = New Dictionary(Of Integer, Integer)(dt.Columns.Count)
			For i As Integer = 0 To dt.Columns.Count - 1
				Dim col As DataColumn = dt.Columns(i)
				If col.ColumnName = "__hash__" Then Continue For
				Dim orig As Integer = original.Columns.IndexOf(col.ColumnName)
				mapping.Add(orig, i)
				If orig <> i Then sameOrder = False
			Next
			If sameOrder Then mapping = Nothing
		End If

		dt.Columns.Add("__hash__", GetType(String))

		'Hash the rows (Except for pk)
		Dim dict As New Dictionary(Of Guid, List(Of String))(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk As String = CAdoData.GetStr(i, 0)

			'Remove pk, fix null
			Dim data As New List(Of Object)(i.ItemArray)
			If Not IsNothing(mapping) Then
				Dim temp As New List(Of Object)(data)
				For j As Integer = 0 To data.Count - 1
					If j >= mapping.Count Then Exit For
					temp(j) = data(mapping(j))
				Next
				data = temp
			End If


			'Fix nulls
			For j As Integer = 0 To data.Count - 1
				If TypeOf (data(j)) Is DBNull Then data(j) = String.Empty
			Next
			For j As Integer = 0 To data.Count - 1
				If IsNothing(data(j)) Then data(j) = String.Empty
				If TypeOf data(j) Is DateTimeOffset Then
					Dim dto As DateTimeOffset = CType(data(j), DateTimeOffset)
					data(j) = dto.ToString()
				End If
			Next

			'Remove pk
			'data.RemoveAt(0)


			'Compute data-only hash
			Dim blob As Byte() = CProto.Serialise(data) 'CSerialise.Pack(data.ToArray, ESerialisation.Protobuf)
			Dim hash As Guid = CBinary.MD5_(blob)
			i("__hash__") = CBinary.ToBase64(hash)

			'Store
			Dim list As List(Of String) = Nothing
			If Not dict.TryGetValue(hash, list) Then
				list = New List(Of String)(1)
				dict.Add(hash, list)
			End If
			list.Add(pk)
		Next
		Return dict
	End Function

	Public Shared Function RowHashing_PkGuid(dt As DataTable, Optional original As DataTable = Nothing) As Dictionary(Of Guid, List(Of Guid))
		'Account for different order of columns
		Dim mapping As Dictionary(Of Integer, Integer) = Nothing
		If Not IsNothing(original) Then
			Dim sameOrder As Boolean = True
			mapping = New Dictionary(Of Integer, Integer)(dt.Columns.Count)
			For i As Integer = 0 To dt.Columns.Count - 1
				Dim col As DataColumn = dt.Columns(i)
				If col.ColumnName = "__hash__" Then Continue For
				Dim orig As Integer = original.Columns.IndexOf(col.ColumnName)
				mapping.Add(orig, i)
				If orig <> i Then sameOrder = False
			Next
			If sameOrder Then mapping = Nothing
		End If

		dt.Columns.Add("__hash__", GetType(String))

		'Hash the rows (Except for pk)
		Dim dict As New Dictionary(Of Guid, List(Of Guid))(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk As Guid = CAdoData.GetGuid(i, 0)

			'Remove pk, fix null
			Dim data As New List(Of Object)(i.ItemArray)
			If Not IsNothing(mapping) Then
				Dim temp As New List(Of Object)(data)
				For j As Integer = 0 To data.Count - 1
					If j >= mapping.Count Then Exit For
					temp(j) = data(mapping(j))
				Next
				data = temp
			End If


			'Fix nulls
			For j As Integer = 0 To data.Count - 1
				If TypeOf (data(j)) Is DBNull Then data(j) = String.Empty
			Next
			For j As Integer = 0 To data.Count - 1
				If IsNothing(data(j)) Then data(j) = String.Empty
				If TypeOf data(j) Is DateTimeOffset Then
					Dim dto As DateTimeOffset = CType(data(j), DateTimeOffset)
					data(j) = dto.ToString()
				End If
			Next

			'Remove pk
			data.RemoveAt(0)

			'Compute data-only hash
			Dim blob As Byte() = CProto.Serialise(data) 'CSerialise.Pack(data.ToArray, ESerialisation.Protobuf)
			Dim hash As Guid = CBinary.MD5_(blob)
			i("__hash__") = CBinary.ToBase64(hash)

			'Store
			Dim list As List(Of Guid) = Nothing
			If Not dict.TryGetValue(hash, list) Then
				list = New List(Of Guid)(1)
				dict.Add(hash, list)
			End If
			list.Add(pk)
		Next
		Return dict
	End Function



	Public Shared Function RowHashing_PkIntInt(dt As DataTable, pkName1 As String, pkName2 As String, Optional original As DataTable = Nothing) As Dictionary(Of Guid, List(Of String))
		'Account for different order of columns
		Dim mapping As Dictionary(Of Integer, Integer) = Nothing
		If Not IsNothing(original) Then
			Dim sameOrder As Boolean = True
			mapping = New Dictionary(Of Integer, Integer)(dt.Columns.Count)
			For i As Integer = 0 To dt.Columns.Count - 1
				Dim col As DataColumn = dt.Columns(i)
				If col.ColumnName = "__hash__" Then Continue For
				Dim orig As Integer = original.Columns.IndexOf(col.ColumnName)
				mapping.Add(orig, i)
				If orig <> i Then sameOrder = False
			Next
			If sameOrder Then mapping = Nothing
		End If

		If Not dt.Columns.Contains("__hash__") Then dt.Columns.Add("__hash__", GetType(String))

		'Hash the rows (Except for pk)
		Dim dict As New Dictionary(Of Guid, List(Of String))(dt.Rows.Count)
		For Each i As DataRow In dt.Rows
			Dim pk1 As Integer = CAdoData.GetInt(i, pkName1)
			Dim pk2 As Integer = CAdoData.GetInt(i, pkName2)
			Dim pk As String = String.Concat(pk1, "_", pk2)

			'Mapp columns
			Dim data As New List(Of Object)(i.ItemArray)
			If Not IsNothing(mapping) Then
				Dim temp As New List(Of Object)(data)
				For j As Integer = 0 To data.Count - 1
					If j >= mapping.Count Then Exit For
					temp(j) = data(mapping(j))
				Next
				data = temp
			End If


			'Fix nulls
			For j As Integer = 0 To data.Count - 1
				If TypeOf (data(j)) Is DBNull Then data(j) = Nothing
				If dt.Columns(j).ColumnName = "TimeStamp" Then data(j) = String.Empty
			Next
			For j As Integer = 0 To data.Count - 1
				If IsNothing(data(j)) Then data(j) = String.Empty
				If TypeOf data(j) Is DateTimeOffset Then
					Dim dto As DateTimeOffset = CType(data(j), DateTimeOffset)
					data(j) = dto.ToString()
				End If
			Next

			'Remove pk
			'Dim idx1 As Integer = dt.Columns.IndexOf(dt.Columns(pkName1))
			'Dim idx2 As Integer = dt.Columns.IndexOf(dt.Columns(pkName2))
			'data.RemoveAt(idx2)
			'data.RemoveAt(idx1)

			'Compute data-only hash
			Dim blob As Byte() = CProto.Serialise(data) 'CSerialise.Pack(data.ToArray, ESerialisation.Protobuf)
			Dim hash As Guid = CBinary.MD5_(blob)
			i("__hash__") = CBinary.ToBase64(hash)

			'Store
			Dim list As List(Of String) = Nothing
			If Not dict.TryGetValue(hash, list) Then
				list = New List(Of String)(1)
				dict.Add(hash, list)
			End If
			list.Add(pk)
		Next
		Return dict
	End Function





	Public Shared Function ReverseHash_PKInt(dict As Dictionary(Of Guid, List(Of Integer))) As Dictionary(Of Integer, Guid)
		Dim reverse As New Dictionary(Of Integer, Guid)
		For Each i As KeyValuePair(Of Guid, List(Of Integer)) In dict
			Dim g As Guid = i.Key
			For Each j As Integer In i.Value
				reverse.Add(j, g)
			Next
		Next
		Return reverse
	End Function
	Public Shared Function ReverseHash_PKLong(dict As Dictionary(Of Guid, List(Of Long))) As Dictionary(Of Long, Guid)
		Dim reverse As New Dictionary(Of Long, Guid)
		For Each i As KeyValuePair(Of Guid, List(Of Long)) In dict
			Dim g As Guid = i.Key
			For Each j As Long In i.Value
				reverse.Add(j, g)
			Next
		Next
		Return reverse
	End Function
	Public Shared Function ReverseHash_PKGuid(dict As Dictionary(Of Guid, List(Of Guid))) As Dictionary(Of Guid, Guid)
		Dim reverse As New Dictionary(Of Guid, Guid)
		For Each i As KeyValuePair(Of Guid, List(Of Guid)) In dict
			Dim g As Guid = i.Key
			For Each j As Guid In i.Value
				reverse.Add(j, g)
			Next
		Next
		Return reverse
	End Function
	Public Shared Function ReverseHash_PKStr(dict As Dictionary(Of Guid, List(Of String))) As Dictionary(Of String, Guid)
		Dim reverse As New Dictionary(Of String, Guid)
		For Each i As KeyValuePair(Of Guid, List(Of String)) In dict
			Dim g As Guid = i.Key
			For Each j As String In i.Value
				reverse.Add(j, g)
			Next
		Next
		Return reverse
	End Function
	Public Shared Function ReverseHash_PKIntInt(dict As Dictionary(Of Guid, List(Of String))) As Dictionary(Of String, Guid)
		Dim reverse As New Dictionary(Of String, Guid)
		For Each i As KeyValuePair(Of Guid, List(Of String)) In dict
			Dim g As Guid = i.Key
			For Each j As String In i.Value
				reverse.Add(j, g)
			Next
		Next
		Return reverse
	End Function
#End Region

End Class
